package commons;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * This is main class being which is called when the user clicks on e^x button
 */
public class CalculatorHarsh {
	
	/** Key=x and Value=e^x */
	static Map<Integer, Double> commonCalc = new ConcurrentHashMap<Integer, Double>();

	public Double expFunc(String str) {
		double x = Double.parseDouble(str); // x= x1 + x2
		int x1 = (int) x; // Integer part of Input.
		double x2 = 0; //decimal part of input.
		double sum1 = 0;
		double sum2 = 0;
		double e = 0;

		
		if (commonCalc.containsKey(1)) { //checking if value for key=1 already exist in Map
			sum1 = commonCalc.get(1);
		} else { // McLaurin series calculation for e^1
			for (int i = 0; i < 21; i++) {
				sum1 += (mul(1, i) / fact(i)); // calculating e^1
			}
			commonCalc.put(1, sum1);//saving value in Map as key=1 and value as e^1
		}
		
		if (commonCalc.containsKey(x1)) { //checking if value for key=x1 already exist in Map
			sum1 = commonCalc.get(x1);
			e=sum1;
		}else { //reducing the amount of calculation
			if (x1 < 0) {
				if(commonCalc.containsKey(2)){ //checking if value for key=2 already exist in Map
					int temp=-x1;
					if(temp%2==0){ //checking if x1 is even
						int multiplier=temp/2;
						sum1=1/(mul(commonCalc.get(2),multiplier));
					}else{ 
						int multiplier=(temp-1)/2;
						sum1=1/(mul(commonCalc.get(2),multiplier)*commonCalc.get(1));
					}
				}else{
					double sum3 = mul(sum1, -x1);
					sum1 = 1 / sum3; // if input negative then taking reciprocal of e^x1
				}
			} else {
				if(commonCalc.containsKey(2)){ //checking if value for key=2 already exist in Map
					if(x1%2==0){
						int multiplier=x1/2;
						sum1=mul(commonCalc.get(2),multiplier);
					}else{
						int multiplier=(x1-1)/2;
						sum1=mul(commonCalc.get(2),multiplier)*commonCalc.get(1);
					}
				}else{
					sum1 = mul(sum1, x1);
				}
			}
			commonCalc.put(x1, sum1);//saving value in Map as key=x1 and value as e^x1
			e = sum1;
		}
		if (x1 != x) {
			x2 = x - x1;// getting fractional/decimal part of input number.its -1 < x2 < 1
			for (int i = 0; i < 21; i++) {
				sum2 += (mul(x2, i) / fact(i)); // calculating e^x2 where -1 < x2 < 1
			}
			e = sum1 * sum2;// e^x = e^x1 * e^x2
		}
		return e;
	}

	private long fact(int n) {

		if (n == 1 || n == 0) {
			return 1;
		} else {
			return (n * fact(n - 1));
		}
	}

	private Double mul(double x, int n) {

		double temp = x;
		if (n == 0) {
			return 1.0;
		} else {
			for (int i = 1; i < n; i++) {
				x *= temp;
			}
			return x;
		}
	}

	/** This method is just for precision test..it doesn't have any part in calculation. */
	@SuppressWarnings("unused")
	private int precision(double x, int precise) {
		int count = 0;
		double p = 0;
		long f = 0;
		double l = 0;
		double r = (1 / mul(10, precise));
		for (int i = 0; i <= 100; i++) {
			p = mul(x, i);
			f = fact(i);
			l = p / f;
			count++;
			if (l < r) {
				break;
			}

		}
		return count;
	}
}
